package com.nbsaas.boot.script.ext.resource;

import com.nbsaas.boot.script.ext.apis.ScriptResource;
import com.nbsaas.boot.script.ext.domain.simple.ScriptSource;

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;

public class HotReloadScriptEngine {

    private final Map<String, CachedScript> cache = new ConcurrentHashMap<>();
    private final Map<String, ScriptResource> sources = new HashMap<>();

    public void addSource(ScriptResource source) {
        if (source.getPrefix() == null || source.getPrefix().isBlank()) {
            throw new IllegalArgumentException("脚本源前缀不能为空");
        }
        if (sources.containsKey(source.getPrefix())) {
            return;
        }
        sources.put(source.getPrefix(), source);
    }


    /**
     * 判断脚本获取处理器是否存在
     */
    public boolean hasSource(String prefix) {
        return sources.containsKey(prefix);
    }

    // Groovy 脚本引擎（通过 JSR-223 获取）
    private final ScriptEngine engine;

    public HotReloadScriptEngine(String language) {
        ServiceLoader<ScriptResource> loader = ServiceLoader.load(ScriptResource.class);
        for (ScriptResource source : loader) {
            sources.put(source.getPrefix(), source);
            System.out.println("加载脚本源: " + source.getPrefix());
        }
        javax.script.ScriptEngineManager factory = new javax.script.ScriptEngineManager();
        this.engine = factory.getEngineByName(language);
        if (!(engine instanceof Compilable)) {
            throw new IllegalStateException("当前脚本引擎不支持编译");
        }
    }

    public Object execute(String path) throws Exception {
        CachedScript cached = getCachedScript(path);
        if (cached == null) {
            return null;
        }
        return cached.script.eval();
    }

    public Object execute(String path, Map<String, Object> context) throws Exception {
        CachedScript cached = getCachedScript(path);
        if (cached == null) {
            return null;
        }
        // 创建绑定上下文
        Bindings bindings = cached.script.getEngine().createBindings();
        if (context != null) {
            bindings.putAll(context);
        }
        return cached.script.eval(bindings);
    }

    private CachedScript getCachedScript(String path) throws Exception {
        ScriptResource source = resolve(path);
        ScriptSource script = source.loadScript(path);
        if (script == null) {
            return null;
        }
        CachedScript cached = cache.get(path);

        // 缓存不存在或者缓存时间小于脚本时间，则重新编译
        if (cached == null || cached.timestamp < script.getTimestamp()) {
            cached = new CachedScript(compile(script.getScript()), System.currentTimeMillis());
            cache.put(path, cached);
        }
        return cached;
    }

    private ScriptResource resolve(String path) {
        return sources.values().stream()
                .filter(s -> path.startsWith(s.getPrefix()))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("未知路径协议: " + path));
    }

    private CompiledScript compile(String script) throws Exception {
        Compilable compilable = (Compilable) engine;
        return compilable.compile(script);
    }

    private record CachedScript(CompiledScript script, long timestamp) {
    }
}